## -*- coding: UTF-8 -*-
"""
This module responds for main application window
"""

import pygtk
import gtk
import os
import sys
import gobject
import pango
import subprocess
import re
import shutil
import traceback

# local modules
from .. import exceptions
from .. import settings
from .. import path
from .. import database
from .. import core
from .. import collection
from .. import bookmarks
from .. import log
from .. import strunicode as su
from . import collectiondirs
from . import editmetadata
from . import about
from . import preferences
from . import browser
from . import managebookmarks

from time import sleep

# module variables
gxml = None # gtk.glade.XML instance
window = None
title_template = u"%s — pybookshelf"
window_default_title = None
progress_vbox = None

# GUI elements
notebook = None
statusbar = None

def initialize():
    """
    Initialize module
    """
    # TODO: check is module initialized
    
    gtk.rc_parse_string('''style "pybookshelf-tab-close-button-style"
    {
        GtkWidget::focus-padding = 0
        GtkWidget::focus-line-width = 0
        xthickness = 0
        ythickness = 0
    }
    widget "*.pybookshelf-tab-close-button" style "pybookshelf-tab-close-button-style"
    ''')

def create_gui():
    """
    Create main window.
    """
    global gxml, window, window_default_title
    
    gxml = gtk.glade.XML(path.get_data_path("pybookshelf.glade"), 
        root="pybookshelf_window", domain="pybookshelf")
    window = gxml.get_widget("pybookshelf_window")
    window_default_title = window.get_title()
    
    init_controls()
    setup_position()
    connect_events()
    connect_accels()

    bookmarks.set_main_bookmarks_menu(gxml.get_widget("bookmarks_menu"))
    on_bookmarks_update()
    
    # initialize related GUI elements
    browser.initialize()
    collectiondirs.initialize(window)
    about.initialize(window)
    preferences.initialize(window)
    managebookmarks.initialize(window)
    editmetadata.initialize(window)

    window.show_all()
    
    on_new_tab()
    
def init_controls():
    """
    Init window controls
    """
    global statusbar, notebook, progress_vbox
    
    statusbar = gxml.get_widget("statusbar")
    
    progress_vbox = gxml.get_widget("progress_vbox")
    progress_vbox.label = gxml.get_widget("progressbar_label")
    progress_vbox.bar = gxml.get_widget("progressbar_progress")
    
    current_page = settings.get_int("ui/last_tab")
    notebook = gxml.get_widget("main_notebook")
    notebook.set_current_page(current_page)

    gxml.get_widget("statistics_item").set_data("target", "stat://general")
    gxml.get_widget("shelves_item").set_data("target", "shelf://")

def setup_position():
    """
    Setup window geometry according to data from settings
    """
    if settings.get_boolean("ui/mainw_maximized"):
        window.maximize()

    settings.restore_window_geometry(window, "main")

def connect_events():
    """
    Connect events to widgets
    """
    window.connect("configure_event", on_resize)
    window.connect("window_state_event", on_state_change)
    window.connect("delete_event", on_quit)

    # menu items
    gxml.get_widget("quit_item").connect("activate", on_quit)
    gxml.get_widget("about_item").connect("activate", on_about)
    gxml.get_widget("new_tab_item").connect("activate", on_new_tab)
    gxml.get_widget("preferences_item").connect("activate", on_preferences)
    gxml.get_widget("compact_db_item").connect("activate", on_compact_db)
    gxml.get_widget("delete_reports_item").connect("activate", on_delete_reports)

    gxml.get_widget("directories_item").connect("activate", on_collection_dirs)
    gxml.get_widget("update_item").connect("activate", on_update)
    gxml.get_widget("export_all_books_item").connect("activate", on_export_all_books_items)
    
    gxml.get_widget("stop_action_button").connect("clicked", lambda *x: collection.cancel_update())

    gxml.get_widget("statistics_item").connect("activate", on_bookmark_activated)
    gxml.get_widget("shelves_item").connect("activate", on_bookmark_activated)

    notebook.connect("switch_page", on_tab_changed)
    bookmarks.get_notifier().connect("bookmarks-changed", on_bookmarks_update)


def connect_accels():
    """
    Connect keyboard accelerators
    """
    accel_group = gtk.AccelGroup()
    window.add_accel_group(accel_group)


## CUT ##
def progressbar_update(fraction, label):
    """
    Updates progressbar value and sets progress label text
    """
    progress_vbox.label.set_text(label)
    progress_vbox.bar.set_fraction(fraction)

def new_browser_tab(url):
    b = browser.Browser(statusbar=statusbar, new_tab_callback=new_browser_tab, 
        top_window=window, url=url)
    box = gtk.HBox(homogeneous=False, spacing=4)
    
    label = gtk.Label(_("Browser"))
    if settings.get_boolean("ui/ellipsis_in_center"):
        ellipsis_mode = pango.ELLIPSIZE_MIDDLE
    else:
        ellipsis_mode = pango.ELLIPSIZE_END
    label.set_ellipsize(ellipsis_mode)
    label.set_single_line_mode(True)
    label.set_alignment(0.0, 0.5)
    label.set_padding(0, 0)
    box.pack_start(label, expand=True, fill=True, padding=0)
    
    # TODO: make icon smaller
    close_button = gtk.Button()
    close_button.set_tooltip_text(_("Close tab"))
    close_button.set_relief(gtk.RELIEF_NONE)
    close_button.set_focus_on_click(False)
    close_button.set_name("pybookshelf-tab-close-button")
    close_button.connect("clicked", on_close_tab, b.widget)

    image = gtk.Image()
    image.set_from_stock(gtk.STOCK_CLOSE, gtk.ICON_SIZE_MENU)

    close_button.add(image)
    box.pack_start(close_button, expand=False, fill=False, padding=0)
    box.show_all()
    
    box.set_data("label", label)
    box.set_data("close-button", close_button)
    
    # stupid gtk code for making tab bar compact
    box.connect("style-set", tab_label_style_set)
    
    insert_after = -1
    if settings.get_boolean("ui/open_new_tab_after_current_one"):
        insert_after = notebook.get_current_page() + 1
        
    tab_index = notebook.insert_page(child=b.widget, position=insert_after)
    notebook.set_tab_label(child=b.widget, tab_label=box)
    notebook.set_tab_label_packing(child=b.widget, expand=True, fill=True, pack_type=gtk.PACK_START)
    # make new tab active
    notebook.set_current_page(tab_index)
    b.connect("title-changed", on_title_tab_changed)
    b.focus_search()
    
def on_title_tab_changed(obj, new_title):
    # find tab
    page_num = notebook.page_num(obj.widget)
    current_page = notebook.get_current_page()
    box = notebook.get_tab_label(obj.widget)
    log.debug("Caught signal 'title-changed' for page `%d'" % page_num)
    if not box is None:
       label = box.get_data("label")
       label.set_text(new_title)
       label.set_tooltip_text(new_title)
       if current_page==page_num:
           # update also top window title
           window.set_title(title_template % new_title)
      
def on_compact_db(menuitem):
    """
    Compact the database (vacuum)
    """
    database.vacuum_database(database.get_connection())
     
def on_delete_reports(menuitem):
    
    ret = core.confirmation(_("Do you really want to delete all files with "
    "extension `*.log' from the directory `%s'?") % path.reports_dir)
    
    if gtk.RESPONSE_YES == ret:
        # delete files
        for file in os.listdir(path.reports_dir):
            fullpath = os.path.join(path.reports_dir, file)
            if os.path.isfile(fullpath) and file.endswith(".log"):
                try:
                    os.remove(fullpath)
                except OSError, e:
                    log.error("OSError occured during removing all report files: %s" % e.message)
    
def on_bookmarks_update(notifier=None):
    """
    Rebuild bookmarks menu
    """
    log.debug("Rebuild bookmarks menu")
    bookmarks.rebuild_bookmarks_menus(on_bookmark_activated, on_manage_bookmarks)
    
def on_manage_bookmarks(menuitem):
    managebookmarks.run_dualog()

def on_bookmark_activated(menuitem):
    """
    Open requested bookmark
    """
    target = menuitem.get_data("content")
    # open requested target in the current tab
    current_page = notebook.get_current_page()
    if current_page == -1:
        new_browser_tab(menuitem.get_data("content"))
        return
    
    page_widget = notebook.get_nth_page(current_page)
    if not isinstance(page_widget.get_data("browser"), browser.Browser):
        return
    
    page_widget.get_data("browser").do_search(menuitem.get_data("target"))
    
def on_resize(widget, event):
    """
        Save the current window geometry
    """
    if settings.get_boolean("ui/mainw_maximized"): 
        return False

    settings.remember_window_geometry(widget, "main")
    
def on_state_change(widget, event):
    if event.changed_mask & gtk.gdk.WINDOW_STATE_MAXIMIZED:
        settings.set_boolean("ui/mainw_maximized",
            bool(event.new_window_state & gtk.gdk.WINDOW_STATE_MAXIMIZED))
    return False

def on_tab_changed(widget, page, page_num):
    settings.set_int("ui/last_tab", page_num)
    # update main window title according to tab title
    if 0==page_num:
        window.set_title(window_default_title)
    else:
        child = notebook.get_nth_page(page_num)
        box = notebook.get_tab_label(child)
        label = box.get_data("label")
        window.set_title(title_template % label.get_text())
    return True

def on_preferences(widget=None, event=None):
    """
        Display preferences dialog
    """
    preferences.run_dialog()

def on_update(widget=None, event=None):
    """
        Rebuild collection database
    """
    progress_vbox.show()
    collection.update_collection(progress_func=progressbar_update, done_func=on_update_done)
    
def on_export_all_books_items(widget=None):
    """
    Export all books displayed on the page
    """
    # first get list of all books in the html page on current tab
    current_page = notebook.get_current_page()
    if current_page == -1:
        # no opened pages
        return
    
    page_widget = notebook.get_nth_page(current_page)
    b = page_widget.get_data("browser")
    
    if not isinstance(b, browser.Browser):
        return
    
    # get html code of displayed HTML page
    books = b.history.get_current_page_books()
    
    if books is None or len(books) == 0:
        return
    
    # display directory chooser
    dialog = gtk.FileChooserDialog(_("Select directory"), parent=window, 
        action=gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER, buttons=None, backend=None)
    
    # get previous used directory
    saved_dir = settings.get_str("paths/multiple_files_save_target_dir")
    saved_dir_dir, saved_dir_filename = os.path.split(saved_dir)
    
    dialog.add_button(_("Cancel"), gtk.RESPONSE_CANCEL)
    dialog.add_button(_("Save"), gtk.RESPONSE_ACCEPT)
    dialog.set_modal(True)
    dialog.set_local_only(True)
    dialog.set_current_folder(saved_dir)
    
    response = dialog.run()
    target_path = dialog.get_filename()
    dialog.destroy()
    
    if gtk.RESPONSE_ACCEPT == response and not target_path is None:
        # save file to the specified location
        saved_files = 0
        total_files = len(books)
        
        rename_exported_books = settings.get_boolean("ui/rename_exported_books")
        rep = []
        
        for b in books:
            # FIXME: Add type checking
            file_id = int(b)

            book_file = collection.get_book_file(file_id)
            # extract filename from full path
            filename = os.path.basename(book_file)

            if rename_exported_books:
                # set new filename
                # get authors list and title
                authors = ", ".join(collection.get_book_authors(file_id))
                title = collection.get_book_title(file_id)
                # get file extension
                from pybs import plugin
                ext = plugin.extract_extension_from_filename(book_file)
                # construct a filename
                filename = u"%s - %s.%s" % (authors, title, ext)
                # check that filename could be converted into filesystem locale
                try:
                    filename = su.unicode2str(filename)
                except UnicodeEncodeError, e:
                    # caught unicode error, so we should try to normalize filename
                    filename = su.normalize(filename)

            target_filename = os.path.join(target_path,filename)
             
            try:
                # book_file is locale encoded string!
                book_file = str(book_file)
                if not os.path.isfile(book_file):
                    msg = "Book file `%s' doesn' exists, skipping." % book_file
                    log.error(msg)
                    rep.append(msg)
                        
                shutil.copyfile(book_file, target_filename)
                saved_files += 1
            except BaseException, e:
                # report exception
                msg = traceback.format_exc()
                log.debug(msg)
                #
                u_book_file = su.str2unicode(book_file)
                msg = _(u"Failed to copy file `%s' to `%s':\n%s\n") % (u_book_file, target_filename, str(e))
                rep.append(msg)
        
        if total_files != saved_files:
            # display warning
            #core.error(_("Not all files were exported successfully (%d of %d)") % (saved_files, total_files), 
            #    parent_window=window)   
            core.detailed_message_box(_("Error"),_("Not all files (%d of %d) were exported successfully") % (total_files-saved_files, total_files),
                "\n".join(rep), parent_window=window) 
        # remember directory
        settings.set_str("paths/multiple_files_save_target_dir", target_path)

def on_update_done(report_file):
    progress_vbox.hide()
    report_viewer = settings.get_str("ui/report_viewer")
    open_report_after_update = settings.get_boolean("ui/open_report_after_update")
    
    if open_report_after_update and report_viewer != "":
        try:
            p = subprocess.Popen([report_viewer, report_file])
        except OSError, e:
            log.error("Failed to execute `%s' to open report, OSError: %s" % (report_viewer, e))
    
def on_collection_dirs(widget=None, event=None):
    """
        Open collection directories preferences dialog.
    """
    if collectiondirs.LIST_UPDATED == collectiondirs.run_dialog():
        # run update collection scenario
        on_update()

def on_quit(widget=None, event=None):
    collection.cancel_update()
    settings.save()
    gtk.main_quit()
    print "Bye!"
    
def on_about(widget=None, event=None):
    about.run_dialog()
    log.debug("Display about dialog")

def on_new_tab(widget=None, event=None):
    # create new browser tab
    new_browser_tab("")
    
def on_close_tab(widget, browser_widget):
    page_num = notebook.page_num(browser_widget)
    browser = browser_widget.get_data("browser")
    
    log.debug("Close tab #%s" % page_num)
    # destroy browser widget
    notebook.remove_page(page_num)
    browser.destroy()
    
def tab_label_style_set(widget, previous_style):
    log.debug("style for tab title requested")
    
    context = widget.get_pango_context()
    metrics = context.get_metrics(widget.style.font_desc, context.get_language())
    char_width = metrics.get_approximate_digit_width()
    # FIXME: is `del' necessary?
    del metrics
    
    s = widget.get_settings()
    w, h = gtk.icon_size_lookup_for_settings(s, gtk.ICON_SIZE_MENU)
    widget.set_size_request(15 * pango.PIXELS(char_width) + 2 * w, -1)
    button = widget.get_data("close-button")
    button.set_size_request(w, h)
